//
//  SalesforceFileSystem.m
//  sffs
//
//  Created by Simon Fell on 3/2/08.
//  Copyright 2008 Simon Fell. All rights reserved.
//

#import "SalesforceFileSystem.h"
#import "zkSforceClient.h"
#import "zkSObject.h"
#import "zkUserInfo.h"
#import "Folder.h"
#import "NSString-Base64Extensions.h"
#import "Preferences.h"

@implementation SalesforceFileSystem

-(id)initWithSforceClient:(ZKSforceClient *)sf {
	self = [super init];
	clientPool = [[Pool alloc] initWithMaster:sf maxPoolItems:5];
	dataMru = [[Mru alloc] init];
	userId = [[[sf currentUserInfo] userId] copy];
	root = [[Folder alloc] initWithName:@"/" id:nil modstamp:[NSDate date] created:[NSDate date] filesystem:self];
	return self;
}

-(void)dealloc {
	[clientPool release];
	[userId release];
	[dataMru release];
	[root release];
	[super dealloc];
}

- (ZKQueryResult *)query:(NSString *)soql {
	ZKSforceClient *sforce = [clientPool checkout];
	@try {
		return [sforce query:soql];
	}
	@finally {
		[clientPool checkin:sforce];
	}
	return nil;
}

- (ZKQueryResult *)queryMore:(NSString *)locator {
	ZKSforceClient *sforce = [clientPool checkout];
	@try {
		return [sforce queryMore:locator];
	}
	@finally {
		[clientPool checkin:sforce];
	}
	return nil;
}

- (NSDictionary *)rootFolders {
	NSMutableDictionary *folders = [NSMutableDictionary dictionary];
	// fake 'folder' for the my personal documents virtual folder
	Folder *myDocs = [[[Folder alloc] initWithName:@"My Personal Documents" id:userId modstamp:[NSDate date] created:[NSDate date] filesystem:self] autorelease];
	[folders setObject:myDocs forKey:[myDocs name]];
	ZKQueryResult *qr = [self query:@"select id, name, CreatedDate, systemModstamp, isReadOnly from Folder where Type='Document'"];
	for(ZKSObject *f in [qr records]) {
		Folder *nf = [[Folder alloc] initWithName:[f fieldValue:@"Name"] id:[f id] modstamp:[f dateTimeValue:@"SystemModstamp"] created:[f dateTimeValue:@"CreatedDate"] filesystem:self];
		[folders setObject:nf forKey:[nf name]];
		[nf release];
	}
	return folders; 
}

- (NSDictionary *)filesForFolder:(NSString *)folderId {
	if (folderId == nil)
		return [self rootFolders];
	ZKQueryResult *qr = [self query:[NSString stringWithFormat:@"select id, name, contentType, bodylength, createdDate, SystemModstamp from Document where Type != 'URL' and FolderId='%@'", folderId]];
	NSMutableDictionary *files = [NSMutableDictionary dictionary];
	while (true) {
		for(ZKSObject *d in [qr records]) {
			File *nf = [[File alloc] initWithName:[d fieldValue:@"Name"] id:[d id] modstamp:[d dateTimeValue:@"SystemModstamp"] created:[d dateTimeValue:@"CreatedDate"] length:[d doubleValue:@"BodyLength"]];
			[files setObject:nf forKey:[nf name]];
			[nf release];
		}
		if ([qr done]) break;
		qr = [self queryMore:[qr queryLocator]];
	}
	return files;
}

- (NSData *)retrieveBodyForDocument:(NSString *)documentId {
	ZKQueryResult *qr = [self query:[NSString stringWithFormat:@"select Body from Document where id='%@'", documentId]];
	if ([qr size] == 0) {
		// got deleted in salesforce, clear out our directory structure cache
		[root clearCache];
		return nil;
	}
	NSString *b64Body = [[[qr records] objectAtIndex:0] fieldValue:@"Body"];
	return [b64Body decodeBase64WithNewlines:NO];
}

- (BOOL)itemHasChanged:(File *)fileItem {
	ZKQueryResult *qr = [self query:[NSString stringWithFormat:@"select SystemModstamp from Document where id='%@'", [fileItem sfdcId]]];
	if ([qr size] == 0) {
		// got deleted in salesforce, clear out our directory structure cache
		[root clearCache];
		return YES;
	}
	ZKSObject *r = [[qr records] objectAtIndex:0];
	NSCalendarDate *sm = [r dateValue:@"SystemModstamp"];
	BOOL changed = [sm timeIntervalSinceReferenceDate] > [[fileItem systemModstamp] timeIntervalSinceReferenceDate];
	return changed;
}

- (NSData *)bodyForDocument:(File *)fileItem {
	NSData *body = nil;
	NSString *documentId = [fileItem sfdcId];
	MruItem *cacheItem = [dataMru mruItemForKey:documentId];
	if (cacheItem != nil) {
		body = [cacheItem item];
		if ([cacheItem isExpired]) {
			if ([self itemHasChanged:fileItem]) {
				body = nil;
			} else {
				[cacheItem resetExpiry];
			}
		}
	}
	if (body == nil) {
		body = [self retrieveBodyForDocument:documentId];
		if (body != nil)
			[dataMru setObject:body forKey:documentId];
	}
	return body;
}

- (FileSystemItem *)fsItemForPath:(NSString *)path {
	if ([path isEqualToString:@"/"])
		return root;
	// eat the leading /
	path = [path substringFromIndex:1];
	// now, if there's a / in it, then it must be directory/file.foo otherwise, its just a directory
	NSArray *pathParts = [path componentsSeparatedByString:@"/"];
	Folder *theFolder = (Folder*)[root fileWithName:[pathParts objectAtIndex:0]];
	if ([pathParts count] == 1 || theFolder == nil)
		return theFolder;

	return [theFolder fileWithName:[pathParts objectAtIndex:1]];
}

// delegate methods

- (NSArray *)contentsOfDirectoryAtPath:(NSString *)path 
                                 error:(NSError **)error {
    return [((Folder*)[self fsItemForPath:path]) filenames];
}

- (NSDictionary *)attributesOfItemAtPath:(NSString *)path 
								   error:(NSError **)error {
    return [[self fsItemForPath:path] attributes];
}	   

- (NSData *)contentsAtPath:(NSString *)path {
	FileSystemItem *fsi = [self fsItemForPath:path];
	if (fsi == nil) return nil;
	if ([fsi isKindOfClass:[File class]])
		return [self bodyForDocument:(File *)fsi];
	return nil;
}
@end
